package com.gitee.ice1938.module.admin.core.operator.fun;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.gitee.ice1938.admin.framwork.annotation.ReqOperation;
import com.gitee.ice1938.module.admin.framwork.AdminContext;
import com.gitee.ice1938.module.admin.framwork.core.MenuItem;
import com.gitee.ice1938.module.admin.framwork.core.Operation;

@Service
public class OperatorServiceImpl implements OperatorService {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(OperatorServiceImpl.class);
	private static final String PART_DIVIDER_TOKEN = ":";
	@Autowired
	private AdminContext adminContext;

	@Autowired
	private MessageSource messageSource;

	@Autowired
	private OperatorRepository operatorRepository;

	@Override
	public Page<MenuItem> queryData(Map<String, Object> param, Pageable pagebale, Order order) {
		order = new Order(Direction.ASC, "parentId");
		Page<MenuItem> source = operatorRepository.queryData(param, pagebale, order);
		List<MenuItem> target = new ArrayList<MenuItem>();
		for (Iterator<MenuItem> iterator = source.iterator(); iterator.hasNext();) {
			MenuItem object = (MenuItem) iterator.next();
			if (object.getParentId() == null) {
				object.setParentId(null);
				target.add(object);
				doSubData(target, object.getId(), source.getContent());
			}
		}
		return new PageImpl<MenuItem>(target, source.getPageable(), source.getTotalElements());
	}

	private void doSubData(List<MenuItem> target, String pid, List<MenuItem> list) {
		for (MenuItem menuItem : list) {
			String pid_ = menuItem.getParentId();
			if (pid_ != null && pid_.equals(pid)) {
				target.add(menuItem);
			}
		}

	}

	/**
	 * 在APP启动后调用
	 */
	@Override
	@Transactional
	public void initOperator() {
		// 获得请求路径信息,获取默认的Controller ，解析全部方法
		// applicationContext.getBeansWithAnnotation(annotationType)
		// 获得所有请求方法 RequestMappingHandlerMapping
		// 通过上下文对象获取RequestMappingHandlerMapping实例对象
		RequestMappingHandlerMapping bean = adminContext.getApplicationContext().getBean(
		        RequestMappingHandlerMapping.class);
		// Map<String, RequestMappingHandlerMapping> requestMappings =
		// BeanFactoryUtils.beansOfTypeIncludingAncestors(
		// applicationContext, RequestMappingHandlerMapping.class, true, false);
		Map<RequestMappingInfo, HandlerMethod> handlerMethods = bean.getHandlerMethods();
		Set<Operation> operations = new HashSet<>();
		for (Entry<RequestMappingInfo, HandlerMethod> item : handlerMethods.entrySet()) {
			HandlerMethod value = item.getValue();
			RequestMappingInfo key = item.getKey();
			RequestMethodsRequestCondition method = key.getMethodsCondition();
			PatternsRequestCondition prc = key.getPatternsCondition();
			Set<String> patterns = prc.getPatterns();
			if (patterns.size() > 1) {
				logger.warn(" MethodAnnotation have more patterns :{}", prc.toString());
				// continue;
			}
			ResponseBody responseBody = value.getMethodAnnotation(ResponseBody.class);
			ReqOperation reqOperation = value.getMethodAnnotation(ReqOperation.class);
			for (String reqUri : patterns) {
				if (reqOperation != null && reqOperation.ignore()) {
					logger.info("reqUri ignored :{}", reqUri);
					continue;
				}
				if (reqUri.endsWith("/")) {
					reqUri = reqUri.substring(0, reqUri.length() - 1);
				}
				String title = null;
				String id = null;
				int position = 0;
				String parentId = "";
				String iconClass = "fa fa-circle-o";
				String capability = null;

				// 不是页面跳转方法
				if (responseBody != null) {
					if (reqUri.split("/").length > 3) {
						parentId = reqUri.split("/")[1] + "_" + reqUri.split("/")[2];
					} else {
						//
						RequestMapping controllerMapping = value.getBeanType().getAnnotation(RequestMapping.class);
						if (controllerMapping == null) {
							// 采用 beanname 定义
							parentId = (String) value.getBean();
							int last_index = parentId.lastIndexOf("Controller");
							parentId = parentId.substring(0, last_index);
						} else {
							// 根据RequestMapping值进行配置
							parentId = controllerMapping.value()[0].replaceFirst("/", "").replace("/", "_");
						}
					}
					parentId = "page_" + operatorRepository.underscoreName(parentId);
					if (id == null || id.isEmpty()) {
						id = reqUri.replaceFirst("/", "").replace("/", "_").replaceAll("\\{\\w*}", "");
					}
					if (reqOperation != null) {
						title = reqOperation.text();
						position = reqOperation.position();
						iconClass = reqOperation.iconClass();
						capability = reqOperation.capability();
					}
					// 含大 小写字母数字和下划线 相当于([0-9a-zA-Z])
					if (title == null || title.isEmpty()) {
						title = reqUri.replaceFirst("/", "page.").replace("/", "_");
					}
					if (capability == null || capability.isEmpty()) {
						capability = reqUri.replaceFirst("/", "").replace("/", ":").replaceAll("\\{\\w*}", "*");
					}
					Operation opt = new Operation();
					opt.setUri(reqUri);
					opt.setId(id).setTitle(title);
					opt.setIconClass(iconClass).setPosition(position);
					opt.setCapability(capability).setParentId(parentId);
					opt.setType_(Operation.Type.button.value());
					operations.add(opt);
				} else {
					if (!method.isEmpty() && !method.getMethods().contains(RequestMethod.GET)) {
						logger.info("reqUri must be GET, {} method:{}", reqUri, method);
						continue;
					}
					// 约定菜单不能包含path变量
					if (reqUri.contains("{")) {
						logger.info("reqUri can't has pathvalue :{},method:{}", reqUri, method);
						continue;
					}
					// 如果没有定义扩展注解
					if (reqOperation == null) {
						// 约定菜单必须是管理功能
						if (!reqUri.startsWith("/admin/")) {
							logger.info("reqUri can't admin path :{},method:{}", reqUri, method);
							continue;
						}
					} else {
						title = reqOperation.text();
						position = reqOperation.position();
						// 仅定义类型未page 时,parent有效-为页面分组的定义
						parentId = reqOperation.group();
						iconClass = reqOperation.iconClass();
						capability = reqOperation.capability();
					}
					// 跳转页面的方法
					MenuItem menu = new MenuItem();
					menu.setUri(reqUri);
					if (reqOperation != null) {
						menu.setTarget(reqOperation.target());
					}
					if (id == null || id.isEmpty()) {
						id = MenuItem.menu_prefix + reqUri.replaceFirst("/", "").replace("/", "_");
					}
					if (title == null || title.isEmpty()) {
						title = reqUri.replaceFirst("/", "menu.").replace("/", "_");
					}
					if (capability == null || capability.isEmpty()) {
						capability = reqUri.replaceFirst("/", "").replace("/", ":");
					}
					if (parentId != null && !parentId.isEmpty()) {
						menu.setParentId(parentId);
					}
					menu.setId(id).setTitle(title);
					menu.setIconClass(iconClass).setPosition(position);
					menu.setCapability(capability);
					menu.setType_(Operation.Type.menu.value());
					operations.add(menu);
					logger.info("add MenuItem :{},method:{}", menu.toString(), method);
				}
			}

		}
		// 注册菜单组 TODO
		// 获得一级菜单
		Map<String, Operation> menuGroupMap = getGroups(operations);
		operations.addAll(menuGroupMap.values());
		operatorRepository.batchInsert(operations);
		for (Operation item : operations) {
			String code = item.getTitle();
			String text = messageSource.getMessage(code, null, null);
			if (code.equals(text)) {
				logger.warn("code [{}] can't finded. ", code);
			}
		}

	}

	private Map<String, Operation> getGroups(Set<Operation> contextMenus) {
		// 构建menugroup(一级菜单)
		Map<String, Operation> menuGroupMap = new HashMap<String, Operation>();
		for (Operation menuItem : contextMenus) {
			String groupId = menuItem.getParentId();
			// null说明是一级菜单，二级菜单是的''
			if (menuItem instanceof MenuItem) {
				if (groupId == null || groupId.isEmpty()) {
					menuGroupMap.putIfAbsent(menuItem.getId(), menuItem);
					continue;
				}
			}
			// -1:页面; 0:目录; 1:菜单 ;2:按钮;
			if (!menuGroupMap.containsKey(menuItem.getParentId())) {
				// 一级菜单采用第一个发现的菜单定义
				Operation opt = new Operation();
				opt.setTitle(menuItem.getParentId().replace("page_", "page."));
				opt.setId(menuItem.getParentId().replace(".", "_"));
				opt.setParentId(null);
				opt.setType_(Operation.Type.page.value());
				if (menuItem.getType() != Operation.Type.dir.value()) {
					opt.setUri(menuItem.getUri().substring(0, menuItem.getUri().lastIndexOf("/")));
				}
				menuGroupMap.putIfAbsent(menuItem.getParentId(), opt);
			}
		}
		if (!menuGroupMap.containsKey("menu_dashboard")) {
			Operation opt = new Operation();
			opt.setId("menu_dashboard");
			opt.setParentId(null);
			opt.setTitle("menu.dashboard");
			opt.setType_(Operation.Type.dir.value());
			menuGroupMap.put("menu_dashboard", opt);
		}
		return menuGroupMap;
	}

	/**
	 * 包括目录和二级菜单
	 */
	public Set<MenuItem> appMenus() {
		// 包括目录和二级菜单
		Set<MenuItem> menus = new HashSet<>();
		List<MenuItem> custom = operatorRepository.findAllMenus();
		menus.addAll(custom);
		return menus;
	}

	@Override
	public int update(String id, String parentId, String iconClass, Integer position) {
		if (parentId == null || parentId.isEmpty()) {
			parentId = null;
		}
		return operatorRepository.update(id, parentId, iconClass, position);
	}

}
